package com.indeep.user.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.indeep.common.emun.RedisKeys;
import com.indeep.common.err.InDeepException;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Map;

@Component("JWTUtils_framework")
public class JWTUtils {
    private static final String APP_TOKEN_SECRET_ACCESS = "ZCfasfhuaUUHufguGuwu2020BQWE";
    
    private static final String APP_TOKEN_SECRET_REFRESH = "F4fF1df484sGSd635se8t5fgrGS";
    
    private static final String APPS_TOKEN_SECRET_ACCESS = "fEfdC925AC468b8A2adEd669a75";
    
    public static final long EXPIRE_TIME_ACCESS = 24 * 60 * 60 * 1000L;  // 1天过期
//    public static final long EXPIRE_TIME_ACCESS = 60 * 1000L;  // 1分钟过期
    
    public static final long EXPIRE_TIME_REFRESH = 30 * 24 * 60 * 60 * 1000L;  // 30天过期
//    public static final long EXPIRE_TIME_REFRESH = 2 * 60 * 1000L;  // 2分钟过期
    

    @Autowired
    RedisTemplate<String,Object> redisTemplate;
    
    //--------------------公域部分--------------------
    // 把map存入payload并生成token
    public static String getAccessToken(Map<String, Object> map) {
        JWTCreator.Builder builder = JWT.create();
        map.forEach((key, value) -> {
            if (value instanceof Boolean) {
                builder.withClaim(key, (Boolean) value);
            } else if (value instanceof Integer) {
                builder.withClaim(key, (Integer) value);
            } else if (value instanceof Long) {
                builder.withClaim(key, (Long) value);
            } else {
                builder.withClaim(key, value.toString());
            }
        });
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME_ACCESS);
        builder.withExpiresAt(date);
        return builder.sign(Algorithm.HMAC256(APP_TOKEN_SECRET_ACCESS));
    }
    
    public static String getAccessToken(Map<String, Object> map, long expireTime) {
        JWTCreator.Builder builder = JWT.create();
        map.forEach((key, value) -> {
            if (value instanceof Boolean) {
                builder.withClaim(key, (Boolean) value);
            } else if (value instanceof Integer) {
                builder.withClaim(key, (Integer) value);
            } else if (value instanceof Long) {
                builder.withClaim(key, (Long) value);
            } else {
                builder.withClaim(key, value.toString());
            }
        });
        if (expireTime >= 0) {
            Date date = new Date(System.currentTimeMillis() + expireTime);
            builder.withExpiresAt(date);
        }
        return builder.sign(Algorithm.HMAC256(APP_TOKEN_SECRET_ACCESS));
    }
    
    // 验证token合法性，不合法将抛出异常
    public static void verifyAccessToken(@NonNull String token) {
        JWT.require(Algorithm.HMAC256(APP_TOKEN_SECRET_ACCESS)).build().verify(token);
    }
    
    // 通过验证token合法性，不合法将抛出异常
    public void verifyAccessTokenRedis(String token) {
        if (token == null) throw new InDeepException("无效的访问令牌");
        DecodedJWT payload = JWTUtils.getDecodeAccessToken(token);
        Long userId = payload.getClaim("userId").asLong();
        String validToken = redisTemplate.opsForValue().get(RedisKeys.VALID_TOKEN_ACCESS + userId).toString();
        if (!token.equals(validToken)) throw new InDeepException("无效的访问令牌");
    }
    
    // 返回一个解码后的token的payload部分
    public static DecodedJWT getDecodeAccessToken(@NonNull String token) {
        return JWT.require(Algorithm.HMAC256(APP_TOKEN_SECRET_ACCESS)).build().verify(token);
    }
    
    /**
     * 把字符串类型的token解析成AppUserToken对象
     *
     * @param token 字符串类型token
     * @return 解析后的AppUserToken对象
     */
    public static AppUserToken getDecodeAppUserToken(String token) {
        DecodedJWT payload = JWTUtils.getDecodeAccessToken(token);
        try {
//            String username = payload.getClaim("username").asString();
            Long userId = payload.getClaim("userId").asLong();
            String tel = payload.getClaim("tel").asString();
            return new AppUserToken(userId, tel);
        } catch (NullPointerException | NumberFormatException e) {
            throw new JWTVerificationException("token版本异常");
        }
    }
    
    public static String getRefreshToken(Map<String, Object> map) {
        JWTCreator.Builder builder = JWT.create();
        map.forEach((key, value) -> {
            if (value instanceof Boolean) {
                builder.withClaim(key, (Boolean) value);
            } else if (value instanceof Integer) {
                builder.withClaim(key, (Integer) value);
            } else if (value instanceof Long) {
                builder.withClaim(key, (Long) value);
            } else {
                builder.withClaim(key, value.toString());
            }
        });
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME_REFRESH);
        builder.withExpiresAt(date);
        
        return builder.sign(Algorithm.HMAC256(APP_TOKEN_SECRET_REFRESH));
    }
    
    /**
     * 验证refreshToken合法性
     *
     * @param refreshToken 要验证合法性的refreshToken
     */
    public void verifyRefreshToken(String refreshToken) {
        // 解码token
        DecodedJWT payload = JWTUtils.getDecodeRefreshToken(refreshToken);
        Long userId = payload.getClaim("userId").asLong();
        
        // 查redis中有无refreshToken
        String validToken = String.valueOf(redisTemplate.opsForValue().get(RedisKeys.VALID_TOKEN_REFRESH + userId));
        if (validToken == null || !validToken.equals(refreshToken)) {
            throw new InDeepException("刷新令牌已过期，请重新登录");
        }
    }
    
    // 返回一个解码后的token的payload部分
    public static DecodedJWT getDecodeRefreshToken(String token) {
        return JWT.require(Algorithm.HMAC256(APP_TOKEN_SECRET_REFRESH)).build().verify(token);
    }
}
